package com.qh.pay.controller;


import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;

import com.qh.common.config.CfgKeyConst;
import com.qh.common.utils.QrZxing;
import com.qh.pay.api.constenum.*;
import com.qh.pay.api.utils.*;
import com.qh.pay.dao.PayOrderDao;
import com.qh.pay.service.PayService;
import com.qh.redis.RedisConstants;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.qh.common.config.Constant;
import com.qh.common.utils.R;
import com.qh.common.utils.ShiroUtils;
import com.qh.pay.api.Order;
import com.qh.pay.api.PayConstants;
import com.qh.pay.domain.PayQrConfigDO;
import com.qh.pay.service.PayQrConfigService;
import com.qh.pay.service.PayQrService;
import com.qh.redis.service.RedisUtil;
import com.qh.system.domain.UserDO;

/**
 * @version 1.0.0
 * @ClassName PayQrController
 * @Description 支付通道扫码支付以及回调
 * @Date 2017年12月19日 下午4:46:40
 */
@Controller
@RequestMapping("/pay")
public class PayQrController {

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(PayQrController.class);
    @Autowired
    private PayService payService;
    @Autowired
    private PayOrderDao payOrderDao;
    @Autowired
    private PayQrService payQrService;
    @Autowired
    private PayQrConfigService payQrConfigService;

    /**
     * @param model
     * @return
     * @Description 人工充值
     */
    @RequiresPermissions("super")
    @GetMapping("/qr/super/charge")
    public String qrSuperCharge(Model model) {
        UserDO user = ShiroUtils.getUser();
        model.addAttribute("username", user.getUsername());
        return PayConstants.url_pay_superCharge;
    }


    /**
     * @return
     * @Description 验证资金密码
     */
    @ResponseBody
    @PostMapping("/qr/checkFundPassword")
    public R checkFundPassword(@RequestParam("fundPassword") String fundPassword) {
        return PasswordCheckUtils.checkFundPassword(fundPassword);
    }

    /**
     * @param model
     * @return
     * @Description 对内授权
     */
    @RequiresPermissions("anyone")
    @GetMapping("/qr/perm/charge")
    public String qrPermCharge(Model model) {
        UserDO user = ShiroUtils.getUser();
        if (UserType.merch.id() != user.getUserType()) {
            model.addAttribute("msg", "非商户不开充值");
            return PayConstants.url_pay_error_frame;
        }
        String merchNo = user.getUsername();

        //组装页面参数
        this.buildChargeParam(model, merchNo);

        return PayConstants.url_pay_charge;
    }

    /**
     * @param context
     * @param model
     * @return
     * @Description 对外加密
     */
    @GetMapping("/qr/charge")
    public String qrCharge(@RequestParam(PayConstants.web_context) String context, Model model) {
        logger.info(PayConstants.web_context + context);
        if (ParamUtil.isNotEmpty(context)) {
            try {
                context = new String(RSAUtil.decryptByPrivateKey(Base64Utils.decode(context),
                        QhPayUtil.getQhPrivateKey()));
            } catch (Exception e) {
                model.addAttribute(Constant.result_msg, "解密异常！");
                return PayConstants.url_pay_error;
            }
            JSONObject jo = JSONObject.parseObject(context);
            String merchNo = jo.getString(OrderParamKey.merchNo.name());
            //组装页面参数
            this.buildChargeParam(model, merchNo);

            return PayConstants.url_pay_charge;
        }
        model.addAttribute(Constant.result_msg, "请勿频繁测试！");
        return PayConstants.url_pay_error;
    }


    /**
     * @param model
     * @param merchNo
     * @Description 组装充值页面参数
     */
    private void buildChargeParam(Model model, String merchNo) {
        model.addAttribute("merchNo", merchNo);
        model.addAttribute("outChannelDesc", OutChannel.jfDesc());
        //按照整数元合并扫码通道的金额
        Map<String, Set<Integer>> monSet = new HashMap<>();
        PayQrConfigDO payQrConfigDO;
        Set<Integer> monIntSet;
        for (String oChannel : OutChannel.jfDesc().keySet()) {
            //资金账号的充值金额
            // ToDO
            String accountNo = null;
            payQrConfigDO = payQrConfigService.get(oChannel, accountNo);
            if (payQrConfigDO == null || payQrConfigDO.getQrs() == null || payQrConfigDO.getQrs().isEmpty()) {
                continue;
            }
            monIntSet = new TreeSet<>();
//        	for (String amountKey : payQrConfigDO.getQrs().keySet()) {
//				if(ParamUtil.isNotEmpty(amountKey)){
//					monIntSet.add(Integer.parseInt(amountKey.split("\\.")[0]));
//				}
//			}
            if (monIntSet.contains(0)) {
                monIntSet.remove(0);
            }
            monSet.put(oChannel, monIntSet);
        }
        model.addAttribute("payAcctBal", RedisUtil.getMerchBal(merchNo));
        model.addAttribute("monSet", monSet);
    }


    /**
     * @param monAmount
     * @param merchNo
     * @param outChannel
     * @return
     * @Description 获取充值金额
     */
    @ResponseBody
    @PostMapping("/qr/getChargeMon")
    public Object getChargeMon(@RequestParam("monAmount") String monAmount, @RequestParam("merchNo") String merchNo,
                               @RequestParam("outChannel") String outChannel) {
        if (ParamUtil.isEmpty(monAmount) || ParamUtil.isEmpty(merchNo) || ParamUtil.isEmpty(outChannel)) {
            logger.error("参数错误：monAmount-{},merchNo-{},outChannel-{}", monAmount, merchNo, outChannel);
            return R.error("请检查参数");
        }
        String accountNo = RedisConstants.default_accountNo;
        return payQrService.getChargeMon(monAmount, merchNo, outChannel);
    }


    /**
     * @return
     * @Description 支付通道扫码界面跳转
     */
    @GetMapping("/qr/real/{merchNo}/{orderNo}")
    public String qrreal(@PathVariable("merchNo") String merchNo,
                         @PathVariable("orderNo") String orderNo, Model model) throws  Exception{
        if (ParamUtil.isEmpty(merchNo) || ParamUtil.isEmpty(orderNo)) {
            model.addAttribute(Constant.result_msg, "订单号或者商户号为空！");
            return PayConstants.url_pay_error;
        }
            Order order = RedisUtil.getOrder(merchNo, orderNo);
//            if (order == null) {
//                R r = payService.query(merchNo, orderNo);
//                model.addAttribute(Constant.result_msg, "支付扫码订单不存在");
//                return PayConstants.url_pay_error;
//            }
//        Order order = RedisUtil.getOrder(merchNo, orderNo);
        if (order == null) {
            order = payOrderDao.get(orderNo, merchNo);
        }
        if (order == null) {
            model.addAttribute(Constant.result_msg, "支付扫码订单不存在");
            return PayConstants.url_pay_error;
        }
        if (OrderState.succ.id() == order.getOrderState()) {
            model.addAttribute(Constant.result_msg, "订单已支付");
            return PayConstants.url_pay_error;
        }
        model.addAttribute("merchNo", merchNo);
        model.addAttribute("orderNo", orderNo);
        model.addAttribute("amount", order.getAmount().toPlainString());
        model.addAttribute("outChannel", order.getOutChannel());
        model.addAttribute("outChannelDesc", OutChannel.jfDesc());
        model.addAttribute("company", order.getPayCompany());
        int remainSec = RedisUtil.getMonAmountOccupyValidTime(order.getOutChannel(), order.getPayMerch());
        model.addAttribute("returnUrl", order.getReturnUrl());
        if (remainSec <= 0) {
            remainSec = 0;
            model.addAttribute("msg", "订单已经过期");
            return PayConstants.url_pay_error;
        } else {
            PayQrConfigDO payQrConfigDo = payQrConfigService.get(order.getOutChannel(), order.getPayMerch());

            model.addAttribute(PayConstants.qr_any_money_flag, YesNoType.yes.id());
            model.addAttribute("qr_url_str", payQrConfigDo.getQrs());
            model.addAttribute("qr_url", "data:image/jpeg;base64," + QrZxing.qrByBase64(payQrConfigDo.getQrs()));

        }
        model.addAttribute("remainSec", remainSec);

        return PayConstants.url_pay_qrmobile;


    }


    public static void main(String[] args) {





    }

    /**
     * @param context
     * @return
     * @Description 支付通道扫码界面跳转
     */
    @GetMapping("/qr")
    public String qr(@RequestParam(PayConstants.web_context) String context, Model model) {
        if (ParamUtil.isNotEmpty(context)) {
            context = AesUtil.decrypt(context, AesUtil.getPay_key());
            if (ParamUtil.isEmpty(context) && !context.contains(RedisConstants.link_symbol)) {
                return PayConstants.url_pay_error;
            }
            String[] datas = context.split(RedisConstants.link_symbol);
            String merchNo = datas[0];
            String orderNo = datas[1];
            if (ParamUtil.isEmpty(merchNo) || ParamUtil.isEmpty(orderNo)) {
                model.addAttribute(Constant.result_msg, "订单号或者商户号为空！");
                return PayConstants.url_pay_error;
            }
//            Order order = RedisUtil.getOrder(merchNo, orderNo);
//            if (order == null) {
//                R r = payService.query(merchNo, orderNo);
//
//                model.addAttribute(Constant.result_msg, "支付扫码订单不存在");
//                return PayConstants.url_pay_error;
//            }
            Order order = RedisUtil.getOrder(merchNo, orderNo);
            if (order == null) {
                order = payOrderDao.get(orderNo, merchNo);
            }
            if (order == null) {
                model.addAttribute(Constant.result_msg, "支付扫码订单不存在");
                return PayConstants.url_pay_error;
            }
            if (OrderState.succ.id() == order.getOrderState()) {
                model.addAttribute(Constant.result_msg, "订单已支付");
                return PayConstants.url_pay_error;
            }
            model.addAttribute("merchNo", merchNo);
            model.addAttribute("orderNo", orderNo);
            model.addAttribute("amount", order.getAmount().toPlainString());
            model.addAttribute("outChannel", order.getOutChannel());
            model.addAttribute("outChannelDesc", OutChannel.jfDesc());
            model.addAttribute("company", order.getPayCompany());
            int remainSec = RedisUtil.getMonAmountOccupyValidTime(order.getOutChannel(), order.getPayMerch());
            model.addAttribute("returnUrl", order.getReturnUrl());
            if (remainSec <= 0) {
                remainSec = 0;
                model.addAttribute("msg", "订单已经过期");
                return PayConstants.url_pay_error;
            } else {
                PayQrConfigDO payQrConfigDo = payQrConfigService.get(order.getOutChannel(), order.getPayMerch());

                model.addAttribute(PayConstants.qr_any_money_flag, YesNoType.yes.id());
                model.addAttribute("qr_url_str", payQrConfigDo.getQrs());
                model.addAttribute("qr_url", "data:image/jpeg;base64," + QrZxing.qrByBase64(payQrConfigDo.getQrs()));

            }
            model.addAttribute("remainSec", remainSec);
            model.addAttribute("url", RedisUtil.getSysConfigValue(CfgKeyConst.pay_domain)+"/pay/qr/real/" + merchNo + "/" + orderNo);
            return PayConstants.url_pay_qr;
        }
        model.addAttribute(Constant.result_msg, "请勿频繁测试！");
        return PayConstants.url_pay_error;
    }


    /**
     * @param outChannel
     * @param accountNo
     * @param request
     * @return
     * @Description 扫码通道充值后台通知
     */
    @PostMapping("/qr/charge/notify/{outChannel}/{accountNo}")
    @ResponseBody
    public JSONObject notifyChargeQr(@PathVariable("outChannel") String outChannel,
                                     @PathVariable("accountNo") String accountNo, HttpServletRequest request) {
        logger.info("扫码通道充值后台通知：{}", outChannel);
        try {
            logger.info("充值扫码通道后台通知参数：{}", RequestUtils.getRequestParam(request).toString());
        } catch (UnsupportedEncodingException e) {
            logger.info("编码错误");
        }
        String monAmount = request.getParameter("amount");
        String businessNo = request.getParameter("tradeNo");

        /**   心跳/支付回调 标记  心跳:timePost   支付回调:up    **/

        String merchNo = RedisUtil.getPayFoundBal().getUsername();
        String todo = request.getParameter("todo");
        if ("timePost".equals(todo)) {
            return syncCheck(merchNo, outChannel, accountNo, request);
        } else if ("up".equals(todo)) {
            //回调验签
            if (verifySign(merchNo, outChannel, accountNo, request)) {
                //业务逻辑处理
                String msg = null;
                try {
                    syncCheck(merchNo, outChannel, accountNo, request);
                    msg = URLDecoder.decode(request.getParameter("time"), "UTF-8") + URLDecoder.decode(request.getParameter("username"), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    logger.error("参数解码失败，{}，{}", "time", "username");
                }
                payQrService.notifyChargeQr(merchNo, outChannel, accountNo, monAmount, businessNo, msg);
            }
        }
        return null;
    }

    /**
     * @return
     * @Description 扫码通道充值后台通知
     */
    @PostMapping("/qr/pay/notifyredis")
    @ResponseBody
    public JSONObject notifyredis(String orderNo,
                                  String orderState,
                                  HttpServletRequest request) {
        logger.info("扫码通道充值后台通知：{},{},{}", orderNo, orderState);
        //业务逻辑处理

        JSONObject jsonObj = new JSONObject();
        jsonObj.put("result", "ok");
        return jsonObj;
    }

    /**
     * @param outChannel
     * @param accountNo
     * @return
     * @Description 扫码通道充值后台通知
     */
    @PostMapping("/qr/pay/notify")
    @ResponseBody
    public JSONObject notifyPayQr(@RequestParam(required = true, value = "outChannel") String outChannel,
                                  @RequestParam(required = true, value = "accountNo") String accountNo,
                                  @RequestParam(required = true, value = "amount") String amount,
                                  @RequestParam(required = true, value = "orderDate") String orderDate,
                                  String msg, HttpServletRequest request) {

        logger.info("扫码通道充值后台通知：{},{},{}", outChannel, accountNo, amount);
        //业务逻辑处理

        JSONObject jsonObj = new JSONObject();
        jsonObj.put("result", "0");
        payQrService.notifyQr(outChannel, accountNo,
                ParamUtil.subZeroAndDot(amount), msg, orderDate);
        logger.info("扫码通道充值后台通知返回：{},{},{}", outChannel, accountNo, ParamUtil.subZeroAndDot(amount));
        return jsonObj;
    }

    /**
     * @param accountNo
     * @return
     * @Description 设备心跳，每30秒一次
     * 如果超过40没有心跳，则判断设备离线
     * 如果设备一上线，更新状态
     */
    @PostMapping("/qr/pay/heart")
    @ResponseBody
    public JSONObject notifyPayQrHeart(
            String accountNo) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("result", "0");
        String accounts[] = accountNo.split(",");
        for (int i = 0; i < accounts.length; i++) {
            String item[] = accounts[i].split("_");
            payQrConfigService.updateQrState("0", item[1], item[0]);
        }
        //业务逻辑处理

        return jsonObj;
    }

    /**
     * @param merchNo
     * @param outChannel
     * @param request
     * @return
     * @Description 扫码通道支付后台通知
     */
    @PostMapping("/qr/notify/{merchNo}/{outChannel}/{accountNo}")
    @ResponseBody
    public JSONObject notifyQr(@PathVariable("merchNo") String merchNo, @PathVariable("outChannel") String outChannel,
                               @PathVariable("accountNo") String accountNo, HttpServletRequest request) {
        logger.info("扫码通道支付后台通知：{},{}", merchNo, outChannel);
        try {
            logger.info("支付扫码通道后台通知参数：{}", RequestUtils.getRequestParam(request).toString());
        } catch (UnsupportedEncodingException e) {
            logger.info("编码错误");
        }
        String monAmount = request.getParameter("amount");
        String businessNo = request.getParameter("tradeNo");

        /**   心跳/支付回调 标记  心跳:timePost   支付回调:up    **/
        String todo = request.getParameter("todo");
        if ("timePost".equals(todo)) {
            return syncCheck(merchNo, outChannel, accountNo, request);
        } else if ("up".equals(todo)) {
            //回调验签
            if (verifySign(merchNo, outChannel, accountNo, request)) {
                //业务逻辑处理
                String msg = null;
                try {
                    syncCheck(merchNo, outChannel, accountNo, request);
                    msg = URLDecoder.decode(request.getParameter("time"), "UTF-8") + URLDecoder.decode(request.getParameter("username"), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    logger.error("参数解码失败，{}，{}", "time", "username");
                }
                payQrService.notifyQr(merchNo, outChannel, accountNo, monAmount, businessNo, msg);
            } else {
                logger.error("支付扫码通道后台通知 验签不通过");
            }
        }
        return null;
    }


    private JSONObject syncCheck(String merchNo, String outChannel, String accountNo, HttpServletRequest request) {
        String v = request.getParameter("v");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        Date date = new Date();
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("S", "0");
        jsonObj.put("msg", "timepost do nothing");
        jsonObj.put("T", sdf.format(date));
        jsonObj.put("V", v);
        RedisUtil.setQrGatewayLastSyncTime(merchNo, outChannel, accountNo, date.getTime());
        logger.info("" + merchNo + "    " + outChannel + "     " + accountNo);
        logger.info("" + RedisUtil.getQrGatewayLastSyncTime(outChannel, accountNo));
        return jsonObj;
    }

    /**
     * @param merchNo
     * @param outChannel
     * @param request
     * @return
     * @Description 验签
     */
    private boolean verifySign(String merchNo, String outChannel, String accountNo, HttpServletRequest request) {
        try {
            PayQrConfigDO payQrConfigDO = payQrConfigService.get(outChannel, accountNo);
            //"4faf5ac89fe24e7dbf64dfa50c2fed7f";//根据商户号获取
            if (payQrConfigDO == null) {
                logger.error("没有对应的APIKEY({},{})", merchNo, outChannel);
                return false;
            }
//			String apiKey = payQrConfigDO.getApiKey();
//			if (StringUtils.isBlank(apiKey)) {
//				logger.error("APIKEY为空({},{})",merchNo,outChannel);
//				return false;
//			}
            TreeMap<String, String> params = RequestUtils.getRequestParam(request);
            String tradeNo = params.get("tradeNo");
            String status = params.get("status");
//			if(outChannel.equals(OutChannel.jfali.name())){
//                tradeNo = URLDecoder.decode(tradeNo,"UTF-8");
//                status = URLDecoder.decode(params.get("status"),"UTF-8");
//            }
            String[] obj = {tradeNo, URLDecoder.decode(params.get("desc"), "UTF-8"), URLDecoder.decode(params.get(
                    "time"), "UTF-8"), URLDecoder.decode(params.get("username"), "UTF-8"), params.get("userid"),
                    params.get("amount"), status};
            String str = String.join("|", obj);
            logger.info(str);
            String sig = params.get("sig");
            String sign = Md5Util.MD5(str);
            logger.info("原签名：" + sig + "；现签名：" + sign);
            if (sign.equalsIgnoreCase(sig)) {
                RedisUtil.setQrGatewayLastSyncTime(merchNo, outChannel, accountNo, new Date().getTime());
                return true;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return false;
    }

}
